/**
 * 当一个属性未定义时，为一个属性赋值
 * @param target
 * @param property
 * @param value
 * @returns
 */
export function assignProppertyIfUndefined<T extends object, P extends keyof T>(
  target: T,
  property: P,
  value: T[P]
): boolean {
  if (target[property] === undefined || target[property] === null) {
    target[property] = value;
    return true;
  }
  return false;
}

export function assignIfUndefined<T extends object>(
  dest: T,
  source: Partial<T>
): void {
  for (const [key, value] of Object.entries(source)) {
    assignProppertyIfUndefined(dest, key as keyof T, value as any);
  }
}

export type OverloadMatch<A extends []> = ((args: A) => boolean) | Function[];

export type OverloadFn<A extends [], R> = (...args: A) => R;

export function overload<
  A1 extends [],
  R1,
  A2 extends [],
  R2,
  A3 extends [] = never,
  R3 = never,
  A4 extends [] = never,
  R4 = never,
  A5 extends [] = never,
  R5 = never
>(
  match1: OverloadMatch<A1>,
  fn1: OverloadFn<A1, R1>,
  match2: OverloadMatch<A2>,
  fn2: OverloadFn<A2, R2>,
  match3: OverloadMatch<A3>,
  fn3: OverloadFn<A3, R3>,
  match4: OverloadMatch<A4>,
  fn4: OverloadFn<A4, R4>,
  match5: OverloadMatch<A5>,
  fn5: OverloadFn<A5, R5>
): {
  (...args: A1): R1;
  (...args: A2): R2;
  (...args: A3): R3;
  (...args: A4): R4;
  (...args: A5): R5;
};
export function overload<
  A1 extends [],
  R1,
  A2 extends [],
  R2,
  A3 extends [] = never,
  R3 = never,
  A4 extends [] = never,
  R4 = never
>(
  match1: OverloadMatch<A1>,
  fn1: OverloadFn<A1, R1>,
  match2: OverloadMatch<A2>,
  fn2: OverloadFn<A2, R2>,
  match3: OverloadMatch<A3>,
  fn3: OverloadFn<A3, R3>,
  match4: OverloadMatch<A4>,
  fn4: OverloadFn<A4, R4>
): {
  (...args: A1): R1;
  (...args: A2): R2;
  (...args: A3): R3;
  (...args: A4): R4;
};
export function overload<
  A1 extends [],
  R1,
  A2 extends [],
  R2,
  A3 extends [] = never,
  R3 = never
>(
  match1: OverloadMatch<A1>,
  fn1: OverloadFn<A1, R1>,
  match2: OverloadMatch<A2>,
  fn2: OverloadFn<A2, R2>,
  match3: OverloadMatch<A3>,
  fn3: OverloadFn<A3, R3>
): {
  (...args: A1): R1;
  (...args: A2): R2;
  (...args: A3): R3;
};
export function overload<A1 extends [], R1, A2 extends [], R2>(
  match1: OverloadMatch<A1>,
  fn1: OverloadFn<A1, R1>,
  match2: OverloadMatch<A2>,
  fn2: OverloadFn<A2, R2>
): {
  (...args: A1): R1;
  (...args: A2): R2;
};
export function overload(...options: any[]): (...args: any) => any {
  return function (this: any, ...args: any) {
    function isMatch(match: Function[]) {
      if (match.length > args.length) return false;
      for (let index = 0; index < match.length; index++) {
        const type = match[index];
        if (
          !(args[index] instanceof type) &&
          args[index]?.constructor !== type
        ) {
          return false;
        }
      }
      return true;
    }

    let i = 0;
    let match: ((...args: any[]) => boolean) | Function[];
    // eslint-disable-next-line no-cond-assign
    while ((match = options[i])) {
      const fn = options[++i] as Function;
      if (typeof fn !== 'function') {
        throw new Error(`Overload must special a function.`);
      }
      if (typeof match === 'function') {
        if (match(args)) {
          return fn.call(this, ...args);
        }
      } else {
        if (isMatch(match)) {
          return fn.call(this, ...args);
        }
      }
      i++;
    }
    throw new Error(`No overload matched by arugments.`);
  };
}

// export interface ExtraFunction {
//   /**
//    * 添加重载
//    */
//   overload<A extends [], R>(selector: (...args: A) => boolean, fn: (...args: A) => R): this & ((...args: A) => R);
// }

// export function q<D extends (...args: any) => any>(fn: D) {
//   return D & {
//     overload:
//   }
// }
